<?php
/*
 * Copyright (c) 2013 Eugene Kuzmenko
 *
 * Permission  is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in  the Software without restriction, including without limitation the rights
 * to  use,  copy,  modify,  merge, publish, distribute, sublicense, and/or sell
 * copies  of  the  Software,  and  to  permit  persons  to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The  above  copyright  notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE  SOFTWARE  IS  PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED,  INCLUDING  BUT  NOT  LIMITED  TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS  FOR  A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS  OR  COPYRIGHT  HOLDERS  BE  LIABLE  FOR  ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 * For more information, see <http://www.aljey.com>.
 */

namespace Al\HTTP\Lib;

use Al\Core\Lib\Service,
    Al\HTTP\Exception\UndefinedRouteException;

/**
 * An HTTP request.
 * 
 * @author Eugene Kuzmenko <mail@aljey.com>
 */
class Request {

  /**
   * Attempts to build a path string based on a route configuration,
   * substituting all the route parameters with the ones provided in the
   * $params or if such a value is missing tries to fallback to the defaults
   * specified in the route and finally attempts to read it from the session.
   * All the unused extra values from the $params array are added as query
   * string parameters.
   *
   * Example:
   * <code>
   * // Acme/Blog/Resources/config/config.yml
   * routes:
   *   blog_post_edit:
   *     path: "{_locale}/blog/post/{id}/edit"
   *     controller: Acme\Blog:post:edit
   *
   * // "/en/blog/post/5/edit"
   * $request->buildRoutePath('blog_post_edit', array('id' => 5));
   * </code>
   *
   * @param string $route_name The name of a route.
   * @param array|\Traversable $params [optional] Route parameters.
   * @return string A path (eg. "/en/blog/post/5/edit").
   * @throws UndefinedRouteException
   * @throws \InvalidArgumentException
   */
  public function buildRoutePath($route_name, $params = array()) {
    if (!($route = Service::get("config.routes.$route_name"))) {
      throw new UndefinedRouteException($route_name);
    }
    return $route->buildPath($params);
  }
  
  /**
   * Routes a request.
   * 
   * Example:
   * <code>
   * $request->route();
   * </code>
   *
   * @return string|void
   * @throws \InvalidArgumentException
   */
  public function route() {
    $params = Service::get('http.params');
    $route  = $params['route'];
    $controller = $route->controller;
    $action = $route->action;
    $ctrl = new $controller;
    $method = new \ReflectionMethod($ctrl, $action);
    $values = array();
    foreach ($method->getParameters() as $param) {
      $name = $param->getName();
      $values[] = isset($params[$name]) ? $params[$name] : null;
    }
    $response = call_user_func_array(array($ctrl, $action), $values);
    if (!($response instanceof Response)) {
      throw new \RuntimeException(
        "$controller::$action must return a Response object");
    }
    if ($params->subrequest) return $response->getData();
    $response->render($route);
  }

  /**
   * Redirects the browser.
   *
   * @param string $route
   * @param array $params
   */
  public function redirect($route, $params = array()) {
    header('Location: ' . $this->buildRoutePath($route, $params));
  }

  /**
   * Effectively reload the current URI.
   */
  public function reload() {
    header('Location: ' . Service::get('http.server.location'));
  }

}